package osSRC.fit_original;

import osSRC.Format;

import java.util.Arrays;
import java.util.LinkedList;

public class Memory {
	static final int MEMORY_LENGTH=128;
	private final boolean[] memory;
	public int used(int index){
		if(index>=memory.length)return -1;
		else if (memory[index])return 1;
		else return 0;
	}
	LinkedList<Area> freeAreas;
	Memory(){
		this(MEMORY_LENGTH);
	}
	Memory(int length){
		memory=new boolean[(int) length];
		Arrays.fill(memory, false);
		this.recordFree();
	}
	void recordFree(){
		freeAreas =null;
		System.gc();
		freeAreas =new LinkedList<>();
		int address=0,length,startaddress;
		while (address< memory.length){
			if (used(address)==0) {
				length=0;
				startaddress = address;
				while (used(address)==0) {
					length++;
					address++;
				}
				freeAreas.add(new Area(startaddress, length));
			}
			address++;
		}
		Area last=freeAreas.getFirst();
		for (Area area:freeAreas){
			if(area==freeAreas.getFirst())			area.last = null;
			else {
				last.next = area;
				if (area == freeAreas.getLast()) 	area.last = null;
				else								area.last = last;
			}
			last=area;
		}
		showFree();
	}
	void allocate(int address ,int size){
		for(int i=0;i<size;i++){
			memory[address+i]=true;
		}
		recordFree();
	}
	int collect(int address,int length){
		boolean collectSuccess=true;
		for (int i=0;i<length;i++){
			switch (used(address+i)){
				case 1:		memory[address+i]=false;	break;
				case 0:
				case -1:	collectSuccess=false;		break;
			}
			if(!collectSuccess)return -1;
		}
		return address;
	}
	void showFree(){
		int i=0;
		System.out.println("Free Areas:");
		for (Area area:freeAreas) System.out.println(
				++i+"\t"
				+area.toString());
	}
	boolean showAllocated(Area allocate,int size){
		if(allocate==null){
			System.out.println("No free area large enough");
			return false;
		}
		else{
			System.out.println("Allocated "+size+"KBs of memory");
			System.out.println("From address:\t"+allocate.getAddress());
			allocate(allocate.getAddress(),size);
			recordFree();
			return true;
		}
//		System.out.println("Allocated areas:");
//		for (int i=0;i<allocatedAreas.size();i++){
//			System.out.print("No.");
//			Format.outputDig(i);
//			System.out.println(allocatedAreas.get(i));
//		}
	}
	boolean firstFit(int size){
		Area allocate=null;
		for(Area area : freeAreas)
			if(area.getLength()>=size) {
				allocate=area;	break;
			}
		return showAllocated(allocate,size);
	}
	boolean bestFit(int size){
		Area allocate=null;
		for(Area area : freeAreas){
			if(area.getLength()>=size) {
				if (allocate==null){
					allocate= area;
				}
				else if (allocate.getLength()> area.getLength()){
					allocate= area;
				}
			}
		}
		return showAllocated(allocate,size);
	}
	boolean worstFit(int size){
		Area allocate=null;
		for(Area area : freeAreas)
			if(area.getLength()>=size) {
				if (allocate==null)
					allocate= area;
				else if (allocate.getLength()<area.getLength())
					allocate= area;
			}
		return showAllocated(allocate,size);
	}
	void requestAreas(){
		int choice;
		String[] strategy={
				"""
Allocate Areas
1.first fit""",
				"2.best fit",
				"3.worst fit",
				"0.return to last menu"
		};
		while (true) {
			choice=Format.selectMenu(strategy,Main.scanner);
			switch (choice) {
				case 1:
					this.firstFit(Format.getInt("Please Enter the Size you want to request:",
							Main.scanner));break;
				case 2:
					this.bestFit(Format.getInt("Please Enter the Size you want to request:",
							Main.scanner));break;
				case 3:
					this.worstFit(Format.getInt("Please Enter the Size you want to request:",
							Main.scanner));break;
				case 0:
					return;
			}
		}
	}
}
